home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / arcers / tarsrc.zip / TARSRC.TAR / tar-1.11.2 / diffarch.c < prev    next >
C/C++ Source or Header  |  1993-11-15  |  18KB  |  767 lines

  1. /* Diff files from a tar archive.
  2.    Copyright (C) 1988, 1992, 1993 Free Software Foundation
  3.  
  4. This file is part of GNU Tar.
  5.  
  6. GNU Tar is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2, or (at your option)
  9. any later version.
  10.  
  11. GNU Tar is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU Tar; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. /*
  21.  * Diff files from a tar archive.
  22.  *
  23.  * Written 30 April 1987 by John Gilmore, ihnp4!hoptoad!gnu.
  24.  */
  25.  
  26. #include <stdio.h>
  27. #include <errno.h>
  28. #ifndef STDC_HEADERS
  29. extern int errno;
  30. #endif
  31. #include <sys/types.h>
  32.  
  33. #ifdef BSD42
  34. #include <sys/file.h>
  35. #else
  36. #ifndef V7
  37. #include <fcntl.h>
  38. #endif
  39. #endif
  40.  
  41. #ifdef HAVE_SYS_MTIO_H
  42. #include <sys/ioctl.h>
  43. #include <sys/mtio.h>
  44. #endif
  45.  
  46. #include "tar.h"
  47. #include "port.h"
  48. #include "rmt.h"
  49.  
  50. #ifndef S_ISLNK
  51. #define lstat stat
  52. #endif
  53.  
  54. extern union record *head;    /* Points to current tape header */
  55. extern struct stat hstat;    /* Stat struct corresponding */
  56. extern int head_standard;    /* Tape header is in ANSI format */
  57.  
  58. #ifndef USE_PROTOTYPES
  59. extern void *valloc ();
  60.  
  61. void decode_header ();
  62. void diff_sparse_files ();
  63. void fill_in_sparse_array ();
  64. void fl_read ();
  65. long from_oct ();
  66. int do_stat ();
  67. extern void print_header ();
  68. int read_header ();
  69. void saverec ();
  70. void sigh ();
  71. extern void skip_file ();
  72. extern void skip_extended_headers ();
  73. int wantbytes ();
  74. #else
  75. #include "diffarch_p.h"
  76. #include "port_p.h"
  77. #include "buffer_p.h"
  78. #include "list_p.h"
  79. #endif
  80.  
  81. extern FILE *msg_file;
  82.  
  83. int now_verifying = 0;        /* Are we verifying at the moment? */
  84.  
  85. int diff_fd;            /* Descriptor of file we're diffing */
  86.  
  87. char *diff_buf = 0;        /* Pointer to area for reading
  88.                        file contents into */
  89.  
  90. char *diff_dir;            /* Directory contents for LF_DUMPDIR */
  91.  
  92. int different = 0;
  93.  
  94. /*struct sp_array *sparsearray;
  95. int         sp_ar_size = 10;*/
  96. /*
  97.  * Initialize for a diff operation
  98.  */
  99. void
  100. diff_init ()
  101. {
  102.   /*NOSTRICT*/
  103.   diff_buf = (char *) valloc ((unsigned) blocksize);
  104.   if (!diff_buf)
  105.     {
  106.       msg ("could not allocate memory for diff buffer of %d bytes",
  107.        blocksize);
  108.       exit (EX_ARGSBAD);
  109.     }
  110. }
  111.  
  112. /*
  113.  * Diff a file against the archive.
  114.  */
  115. void
  116. diff_archive ()
  117. {
  118.   register char *data;
  119.   int check, namelen;
  120.   int err;
  121.   long offset;
  122.   struct stat filestat;
  123.   int compare_chunk ();
  124.   int compare_dir ();
  125.   int no_op ();
  126. #ifndef __MSDOS__
  127.   dev_t dev;
  128.   ino_t ino;
  129. #endif
  130.   char *get_dir_contents ();
  131.   long from_oct ();
  132.  
  133.   errno = EPIPE;        /* FIXME, remove perrors */
  134.  
  135.   saverec (&head);        /* Make sure it sticks around */
  136.   userec (head);        /* And go past it in the archive */
  137.   decode_header (head, &hstat, &head_standard, 1);    /* Snarf fields */
  138.  
  139.   /* Print the record from 'head' and 'hstat' */
  140.   if (f_verbose)
  141.     {
  142.       if (now_verifying)
  143.     fprintf (msg_file, "Verify ");
  144.       print_header ();
  145.     }
  146.  
  147.   switch (head->header.linkflag)
  148.     {
  149.  
  150.     default:
  151.       msg ("Unknown file type '%c' for %s, diffed as normal file",
  152.        head->header.linkflag, current_file_name);
  153.       /* FALL THRU */
  154.  
  155.     case LF_OLDNORMAL:
  156.     case LF_NORMAL:
  157.     case LF_SPARSE:
  158.     case LF_CONTIG:
  159.       /*
  160.          * Appears to be a file.
  161.          * See if it's really a directory.
  162.          */
  163.       namelen = strlen (current_file_name) - 1;
  164.       if (current_file_name[namelen] == '/')
  165.     goto really_dir;
  166.  
  167.  
  168.       if (do_stat (&filestat))
  169.     {
  170.       if (head->header.isextended)
  171.         skip_extended_headers ();
  172.       skip_file ((long) hstat.st_size);
  173.       different++;
  174.       goto quit;
  175.     }
  176.  
  177.       if (!S_ISREG (filestat.st_mode))
  178.     {
  179.       fprintf (msg_file, "%s: not a regular file\n",
  180.            current_file_name);
  181.       skip_file ((long) hstat.st_size);
  182.       different++;
  183.       goto quit;
  184.     }
  185.  
  186.       filestat.st_mode &= 07777;
  187.       if (filestat.st_mode != hstat.st_mode)
  188.     sigh ("mode");
  189.       if (filestat.st_uid != hstat.st_uid)
  190.     sigh ("uid");
  191.       if (filestat.st_gid != hstat.st_gid)
  192.     sigh ("gid");
  193.       if (filestat.st_mtime != hstat.st_mtime)
  194.     sigh ("mod time");
  195.       if (head->header.linkflag != LF_SPARSE &&
  196.       filestat.st_size != hstat.st_size)
  197.     {
  198.       sigh ("size");
  199.       skip_file ((long) hstat.st_size);
  200.       goto quit;
  201.     }
  202.  
  203.       diff_fd = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);
  204.  
  205.       if (diff_fd < 0 && !f_absolute_paths)
  206.     {
  207.       char tmpbuf[NAMSIZ + 2];
  208.  
  209.       tmpbuf[0] = '/';
  210.       strcpy (&tmpbuf[1], current_file_name);
  211.       diff_fd = open (tmpbuf, O_NDELAY | O_RDONLY);
  212.     }
  213.       if (diff_fd < 0)
  214.     {
  215.       msg_perror ("cannot open %s", current_file_name);
  216.       if (head->header.isextended)
  217.         skip_extended_headers ();
  218.       skip_file ((long) hstat.st_size);
  219.       different++;
  220.       goto quit;
  221.     }
  222.       /*
  223.          * Need to treat sparse files completely differently here.
  224.          */
  225.       if (head->header.linkflag == LF_SPARSE)
  226.     diff_sparse_files (hstat.st_size);
  227.       else
  228.     wantbytes ((long) (hstat.st_size), compare_chunk);
  229.  
  230.       check = close (diff_fd);
  231.       if (check < 0)
  232.     msg_perror ("Error while closing %s", current_file_name);
  233.  
  234.     quit:
  235.       break;
  236.  
  237. #ifndef __MSDOS__
  238.     case LF_LINK:
  239.       if (do_stat (&filestat))
  240.     break;
  241.       dev = filestat.st_dev;
  242.       ino = filestat.st_ino;
  243.       err = stat (current_link_name, &filestat);
  244.       if (err < 0)
  245.     {
  246.       if (errno == ENOENT)
  247.         {
  248.           fprintf (msg_file, "%s: does not exist\n", current_file_name);
  249.         }
  250.       else
  251.         {
  252.           msg_perror ("cannot stat file %s", current_file_name);
  253.         }
  254.       different++;
  255.       break;
  256.     }
  257.       if (filestat.st_dev != dev || filestat.st_ino != ino)
  258.     {
  259.       fprintf (msg_file, "%s not linked to %s\n", current_file_name, current_link_name);
  260.       break;
  261.     }
  262.       break;
  263. #endif
  264.  
  265. #ifdef S_ISLNK
  266.     case LF_SYMLINK:
  267.       {
  268.     char linkbuf[NAMSIZ + 3];
  269.     check = readlink (current_file_name, linkbuf,
  270.               (sizeof linkbuf) - 1);
  271.  
  272.     if (check < 0)
  273.       {
  274.         if (errno == ENOENT)
  275.           {
  276.         fprintf (msg_file,
  277.              "%s: no such file or directory\n",
  278.              current_file_name);
  279.           }
  280.         else
  281.           {
  282.         msg_perror ("cannot read link %s", current_file_name);
  283.           }
  284.         different++;
  285.         break;
  286.       }
  287.  
  288.     linkbuf[check] = '\0';    /* Null-terminate it */
  289.     if (strncmp (current_link_name, linkbuf, check) != 0)
  290.       {
  291.         fprintf (msg_file, "%s: symlink differs\n",
  292.              current_link_name);
  293.         different++;
  294.       }
  295.       }
  296.       break;
  297. #endif
  298.  
  299. #ifdef S_IFCHR
  300.     case LF_CHR:
  301.       hstat.st_mode |= S_IFCHR;
  302.       goto check_node;
  303. #endif
  304.  
  305. #ifdef S_IFBLK
  306.       /* If local system doesn't support block devices, use default case */
  307.     case LF_BLK:
  308.       hstat.st_mode |= S_IFBLK;
  309.       goto check_node;
  310. #endif
  311.  
  312. #ifdef S_ISFIFO
  313.       /* If local system doesn't support FIFOs, use default case */
  314.     case LF_FIFO:
  315. #ifdef S_IFIFO
  316.       hstat.st_mode |= S_IFIFO;
  317. #endif
  318.       hstat.st_rdev = 0;    /* FIXME, do we need this? */
  319.       goto check_node;
  320. #endif
  321.  
  322.     check_node:
  323.       /* FIXME, deal with umask */
  324.       if (do_stat (&filestat))
  325.     break;
  326.       if (hstat.st_rdev != filestat.st_rdev)
  327.     {
  328.       fprintf (msg_file, "%s: device numbers changed\n", current_file_name);
  329.       different++;
  330.       break;
  331.     }
  332. #ifdef S_IFMT
  333.       if (hstat.st_mode != filestat.st_mode)
  334. #else /* POSIX lossage */
  335.       if ((hstat.st_mode & 07777) != (filestat.st_mode & 07777))
  336. #endif
  337.     {
  338.       fprintf (msg_file, "%s: mode or device-type changed\n", current_file_name);
  339.       different++;
  340.       break;
  341.     }
  342.       break;
  343.  
  344.     case LF_DUMPDIR:
  345.       data = diff_dir = get_dir_contents (current_file_name, 0);
  346.       if (data)
  347.     {
  348.       wantbytes ((long) (hstat.st_size), compare_dir);
  349.       free (data);
  350.     }
  351.       else
  352.     wantbytes ((long) (hstat.st_size), no_op);
  353.       /* FALL THROUGH */
  354.  
  355.     case LF_DIR:
  356.       /* Check for trailing / */
  357.       namelen = strlen (current_file_name) - 1;
  358.     really_dir:
  359.       while (namelen && current_file_name[namelen] == '/')
  360.     current_file_name[namelen--] = '\0';    /* Zap / */
  361.  
  362.       if (do_stat (&filestat))
  363.     break;
  364.       if (!S_ISDIR (filestat.st_mode))
  365.     {
  366.       fprintf (msg_file, "%s is no longer a directory\n", current_file_name);
  367.       different++;
  368.       break;
  369.     }
  370.       if ((filestat.st_mode & 07777) != (hstat.st_mode & 07777))
  371.     sigh ("mode");
  372.       break;
  373.  
  374.     case LF_VOLHDR:
  375.       break;
  376.  
  377.     case LF_MULTIVOL:
  378.       namelen = strlen (current_file_name) - 1;
  379.       if (current_file_name[namelen] == '/')
  380.     goto really_dir;
  381.  
  382.       if (do_stat (&filestat))
  383.     break;
  384.  
  385.       if (!S_ISREG (filestat.st_mode))
  386.     {
  387.       fprintf (msg_file, "%s: not a regular file\n",
  388.            current_file_name);
  389.       skip_file ((long) hstat.st_size);
  390.       different++;
  391.       break;
  392.     }
  393.  
  394.       filestat.st_mode &= 07777;
  395.       offset = from_oct (1 + 12, head->header.offset);
  396.       if (filestat.st_size != hstat.st_size + offset)
  397.     {
  398.       sigh ("size");
  399.       skip_file ((long) hstat.st_size);
  400.       different++;
  401.       break;
  402.     }
  403.  
  404.       diff_fd = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);
  405.  
  406.       if (diff_fd < 0)
  407.     {
  408.       msg_perror ("cannot open file %s", current_file_name);
  409.       skip_file ((long) hstat.st_size);
  410.       different++;
  411.       break;
  412.     }
  413.       err = lseek (diff_fd, offset, 0);
  414.       if (err != offset)
  415.     {
  416.       msg_perror ("cannot seek to %ld in file %s", offset, current_file_name);
  417.       different++;
  418.       break;
  419.     }
  420.  
  421.       wantbytes ((long) (hstat.st_size), compare_chunk);
  422.  
  423.       check = close (diff_fd);
  424.       if (check < 0)
  425.     {
  426.       msg_perror ("Error while closing %s", current_file_name);
  427.     }
  428.       break;
  429.  
  430.     }
  431.  
  432.   /* We don't need to save it any longer. */
  433.   saverec ((union record **) 0);/* Unsave it */
  434. }
  435.  
  436. int
  437. compare_chunk (bytes, buffer)
  438.      long bytes;
  439.      char *buffer;
  440. {
  441.   int err;
  442.  
  443.   err = read (diff_fd, diff_buf, bytes);
  444.   if (err != bytes)
  445.     {
  446.       if (err < 0)
  447.     {
  448.       msg_perror ("can't read %s", current_file_name);
  449.     }
  450.       else
  451.     {
  452.       fprintf (msg_file, "%s: could only read %d of %d bytes\n", current_file_name, err, bytes);
  453.     }
  454.       different++;
  455.       return -1;
  456.     }
  457.   if (bcmp (buffer, diff_buf, bytes))
  458.     {
  459.       fprintf (msg_file, "%s: data differs\n", current_file_name);
  460.       different++;
  461.       return -1;
  462.     }
  463.   return 0;
  464. }
  465.  
  466. int
  467. compare_dir (bytes, buffer)
  468.      long bytes;
  469.      char *buffer;
  470. {
  471.   if (bcmp (buffer, diff_dir, bytes))
  472.     {
  473.       fprintf (msg_file, "%s: data differs\n", current_file_name);
  474.       different++;
  475.       return -1;
  476.     }
  477.   diff_dir += bytes;
  478.   return 0;
  479. }
  480.  
  481. /*
  482.  * Sigh about something that differs.
  483.  */
  484. void
  485. sigh (what)
  486.      char *what;
  487. {
  488.  
  489.   fprintf (msg_file, "%s: %s differs\n",
  490.        current_file_name, what);
  491. }
  492.  
  493. void
  494. verify_volume ()
  495. {
  496.   int status;
  497. #ifdef MTIOCTOP
  498.   struct mtop t;
  499.   int er;
  500. #endif
  501.  
  502.   if (!diff_buf)
  503.     diff_init ();
  504. #ifdef MTIOCTOP
  505.   t.mt_op = MTBSF;
  506.   t.mt_count = 1;
  507.   if ((er = rmtioctl (archive, MTIOCTOP, &t)) < 0)
  508.     {
  509.       if (errno != EIO || (er = rmtioctl (archive, MTIOCTOP, &t)) < 0)
  510.     {
  511. #endif
  512.       if (rmtlseek (archive, 0L, 0) != 0)
  513.         {
  514.           /* Lseek failed.  Try a different method */
  515.           msg_perror ("Couldn't rewind archive file for verify");
  516.           return;
  517.         }
  518. #ifdef MTIOCTOP
  519.     }
  520.     }
  521. #endif
  522.   ar_reading = 1;
  523.   now_verifying = 1;
  524.   fl_read ();
  525.   for (;;)
  526.     {
  527.       status = read_header ();
  528.       if (status == 0)
  529.     {
  530.       unsigned n;
  531.  
  532.       n = 0;
  533.       do
  534.         {
  535.           n++;
  536.           status = read_header ();
  537.         }
  538.       while (status == 0);
  539.       msg ("VERIFY FAILURE: %d invalid header%s detected!", n, n == 1 ? "" : "s");
  540.     }
  541.       if (status == 2 || status == EOF)
  542.     break;
  543.       diff_archive ();
  544.     }
  545.   ar_reading = 0;
  546.   now_verifying = 0;
  547.  
  548. }
  549.  
  550. int
  551. do_stat (statp)
  552.      struct stat *statp;
  553. {
  554.   int err;
  555.  
  556.   err = f_follow_links ? stat (current_file_name, statp) : lstat (current_file_name, statp);
  557.   if (err < 0)
  558.     {
  559.       if (errno == ENOENT)
  560.     {
  561.       fprintf (msg_file, "%s: does not exist\n", current_file_name);
  562.     }
  563.       else
  564.     msg_perror ("can't stat file %s", current_file_name);
  565.       /*        skip_file((long)hstat.st_size);
  566.         different++;*/
  567.       return 1;
  568.     }
  569.   else
  570.     return 0;
  571. }
  572.  
  573. /*
  574.  * JK
  575.  * Diff'ing a sparse file with its counterpart on the tar file is a
  576.  * bit of a different story than a normal file.  First, we must know
  577.  * what areas of the file to skip through, i.e., we need to contruct
  578.  * a sparsearray, which will hold all the information we need.  We must
  579.  * compare small amounts of data at a time as we find it.
  580.  */
  581.  
  582. void
  583. diff_sparse_files (filesize)
  584.      int filesize;
  585.  
  586. {
  587.   int sparse_ind = 0;
  588.   char *buf;
  589.   int buf_size = RECORDSIZE;
  590.   union record *datarec;
  591.   int err;
  592.   long numbytes;
  593.   /*    int        amt_read = 0;*/
  594.   int size = filesize;
  595.  
  596.   buf = (char *) ck_malloc (buf_size * sizeof (char));
  597.  
  598.   fill_in_sparse_array ();
  599.  
  600.  
  601.   while (size > 0)
  602.     {
  603.       datarec = findrec ();
  604.       if (!sparsearray[sparse_ind].numbytes)
  605.     break;
  606.  
  607.       /*
  608.          * 'numbytes' is nicer to write than
  609.          * 'sparsearray[sparse_ind].numbytes' all the time ...
  610.          */
  611.       numbytes = sparsearray[sparse_ind].numbytes;
  612.  
  613.       lseek (diff_fd, sparsearray[sparse_ind].offset, 0);
  614.       /*
  615.          * take care to not run out of room in our buffer
  616.          */
  617.       while (buf_size < numbytes)
  618.     {
  619.       buf = (char *) ck_realloc (buf, buf_size * 2 * sizeof (char));
  620.       buf_size *= 2;
  621.     }
  622.       while (numbytes > RECORDSIZE)
  623.     {
  624.       if ((err = read (diff_fd, buf, RECORDSIZE)) != RECORDSIZE)
  625.         {
  626.           if (err < 0)
  627.         msg_perror ("can't read %s", current_file_name);
  628.           else
  629.         fprintf (msg_file, "%s: could only read %d of %d bytes\n",
  630.              current_file_name, err, numbytes);
  631.           break;
  632.         }
  633.       if (bcmp (buf, datarec->charptr, RECORDSIZE))
  634.         {
  635.           different++;
  636.           break;
  637.         }
  638.       numbytes -= err;
  639.       size -= err;
  640.       userec (datarec);
  641.       datarec = findrec ();
  642.     }
  643.       if ((err = read (diff_fd, buf, numbytes)) != numbytes)
  644.     {
  645.       if (err < 0)
  646.         msg_perror ("can't read %s", current_file_name);
  647.       else
  648.         fprintf (msg_file, "%s: could only read %d of %d bytes\n",
  649.              current_file_name, err, numbytes);
  650.       break;
  651.     }
  652.  
  653.       if (bcmp (buf, datarec->charptr, numbytes))
  654.     {
  655.       different++;
  656.       break;
  657.     }
  658.       /*        amt_read += numbytes;
  659.         if (amt_read >= RECORDSIZE) {
  660.             amt_read = 0;
  661.             userec(datarec);
  662.             datarec = findrec();
  663.         }*/
  664.       userec (datarec);
  665.       sparse_ind++;
  666.       size -= numbytes;
  667.     }
  668.   /*
  669.      * if the number of bytes read isn't the
  670.      * number of bytes supposedly in the file,
  671.      * they're different
  672.      */
  673.   /*    if (amt_read != filesize)
  674.         different++;*/
  675.   userec (datarec);
  676.   free (sparsearray);
  677.   if (different)
  678.     fprintf (msg_file, "%s: data differs\n", current_file_name);
  679.  
  680. }
  681.  
  682. /*
  683.  * JK
  684.  * This routine should be used more often than it is ... look into
  685.  * that.  Anyhow, what it does is translate the sparse information
  686.  * on the header, and in any subsequent extended headers, into an
  687.  * array of structures with true numbers, as opposed to character
  688.  * strings.  It simply makes our life much easier, doing so many
  689.  * comparisong and such.
  690.  */
  691. void
  692. fill_in_sparse_array ()
  693. {
  694.   int ind;
  695.  
  696.   /*
  697.      * allocate space for our scratch space; it's initially
  698.      * 10 elements long, but can change in this routine if
  699.      * necessary
  700.      */
  701.   sp_array_size = 10;
  702.   sparsearray = (struct sp_array *) ck_malloc (sp_array_size * sizeof (struct sp_array));
  703.  
  704.   /*
  705.      * there are at most five of these structures in the header
  706.      * itself; read these in first
  707.      */
  708.   for (ind = 0; ind < SPARSE_IN_HDR; ind++)
  709.     {
  710.       if (!head->header.sp[ind].numbytes)
  711.     break;
  712.       sparsearray[ind].offset =
  713.     from_oct (1 + 12, head->header.sp[ind].offset);
  714.       sparsearray[ind].numbytes =
  715.     from_oct (1 + 12, head->header.sp[ind].numbytes);
  716.     }
  717.   /*
  718.      * if the header's extended, we gotta read in exhdr's till
  719.      * we're done
  720.      */
  721.   if (head->header.isextended)
  722.     {
  723.       /* how far into the sparsearray we are 'so far' */
  724.       static int so_far_ind = SPARSE_IN_HDR;
  725.       union record *exhdr;
  726.  
  727.       for (;;)
  728.     {
  729.       exhdr = findrec ();
  730.       for (ind = 0; ind < SPARSE_EXT_HDR; ind++)
  731.         {
  732.           if (ind + so_far_ind > sp_array_size - 1)
  733.         {
  734.           /*
  735.                   * we just ran out of room in our
  736.                  *  scratch area - realloc it
  737.                   */
  738.           sparsearray = (struct sp_array *)
  739.             ck_realloc (sparsearray,
  740.                  sp_array_size * 2 * sizeof (struct sp_array));
  741.           sp_array_size *= 2;
  742.         }
  743.           /*
  744.              * convert the character strings into longs
  745.              */
  746.           sparsearray[ind + so_far_ind].offset =
  747.         from_oct (1 + 12, exhdr->ext_hdr.sp[ind].offset);
  748.           sparsearray[ind + so_far_ind].numbytes =
  749.         from_oct (1 + 12, exhdr->ext_hdr.sp[ind].numbytes);
  750.         }
  751.       /*
  752.          * if this is the last extended header for this
  753.          * file, we can stop
  754.          */
  755.       if (!exhdr->ext_hdr.isextended)
  756.         break;
  757.       else
  758.         {
  759.           so_far_ind += SPARSE_EXT_HDR;
  760.           userec (exhdr);
  761.         }
  762.     }
  763.       /* be sure to skip past the last one  */
  764.       userec (exhdr);
  765.     }
  766. }
  767.